package pl.enigmatic.time;

import java.util.Comparator;


/**
 * Represents the duration of time that a Timeline is active.
 * @author reggie_7
 * @see <a href="http://msdn.microsoft.com/en-us/library/system.windows.duration.aspx">msdn reference</a>
 */
public class Duration implements Comparator<Duration> {

	public static enum Type {
		FOREVER, AUTOMATIC, TIMESPAN,
	}

	/** a Duration value that is automatically determined */
	public static final Duration AUTOMATIC = new Duration(Type.AUTOMATIC, null);
	/** a Duration value that represents an infinite interval */
	public static final Duration FOREVER = new Duration(Type.FOREVER, null);

	/**
	 * Determines whether two instances of Duration are equal.
	 * @param t1 duratio
	 * @param t2 duratio
	 * @return true if t1 is equal to t2 ; otherwise, false .
	 */
	public static boolean equals(final Duration t1, final Duration t2) {
		return t1 == t2 || t1 != null && t1.equals(t2);
	}

	private final Type type;
	/** the TimeSpan value that this Duration represents */
	private final TimeSpan timeSpan;

	private Duration(final Type type, final TimeSpan timeSpan) {
		this.type = type;
		this.timeSpan = timeSpan;
	}

	/**
	 * Initializes a new instance of the Duration structure with the supplied TimeSpan value.
	 * @param timeSpan represents the initial time interval of this duration
	 */
	public Duration(final TimeSpan timeSpan) {
		this(Type.TIMESPAN, timeSpan == null ? new TimeSpan(0) : timeSpan);
	}

	public boolean isForever() {
		return type == Type.FOREVER;
	}

	public boolean isAutomatic() {
		return type == Type.AUTOMATIC;
	}

	/** @return a value that indicates if this Duration represents a TimeSpan value */
	public boolean hasTimeSpan() {
		return type == Type.TIMESPAN;
	}

	/** @return the TimeSpan value that this Duration represents */
	public TimeSpan getTimeSpan() {
		return timeSpan;
	}

	/**
	 * Adds the value of the specified instance of Duration to the value of the current instance.
	 * @param duration duratio
	 * @return if both instances of Duration have values, an instance of Duration that represents the combined values. Otherwise this method returns
	 *         null.
	 */
	public Duration add(final Duration other) {
		if (hasTimeSpan() && other != null && other.hasTimeSpan()) { return new Duration(timeSpan.add(other.timeSpan)); }
		return null;
	}

	/**
	 * Compares one Duration value to another.
	 * @param t1 duratio
	 * @param t2 duratio
	 * @return if t1 is less than t2 , a negative value that represents the difference. If t1 is equal to t2, zero. If t1 is greater than t2 , a
	 *         positive value that represents the difference.
	 */
	@Override
	public int compare(final Duration t1, final Duration t2) {
		if (t1.hasTimeSpan() && t2.hasTimeSpan()) { return t1.timeSpan.compareTo(t2.timeSpan); }
		return t1.type.compareTo(t2.type);
	}

	/**
	 * Determines whether a specified Duration is equal to this instance of Duration.
	 * @param duration duratio
	 * @return true if duration is equal to the current instance of Duration ; otherwise, false .
	 */
	public boolean equals(final Duration other) {
		if (type != other.type) { return false; }
		return hasTimeSpan() ? timeSpan.equals(other.timeSpan) : true;
	}

	/**
	 * Determines whether a specified object is equal to an instance of Duration .
	 * @param value object to check for equality
	 * @return true if value is equal to the current instance of Duration; otherwise, false .
	 */
	@Override
	public boolean equals(final Object value) {
		if (value instanceof Duration) { return equals((Duration) value); }
		return false;
	}

	/**
	 * Subtracts the value of the specified instance of Duration from this instance.
	 * @param duration duratio
	 * @return a new instance of Duration whose value is the result of this instance minus the value of duration .
	 */
	public void subtract(final Duration other) {
		if (hasTimeSpan() && other != null && other.hasTimeSpan()) {
			timeSpan.subtract(other.timeSpan);
		}
	}

	@Override
	public String toString() {
		return getClass().getSimpleName() + " [" + (type != null ? "type=" + type + ", " : "") + (timeSpan != null ? "timeSpan=" + timeSpan : "") + "]";
	}

}